package com.cqupt.service.impl;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.cqupt.component.BigModelStreamClient;
import com.cqupt.component.MemoryUserRecordSpace;
import com.cqupt.config.BigModelConfig;
import com.cqupt.exception.BusinessException;
import com.cqupt.listener.BigModelWebSocketListener;
import com.cqupt.model.dto.InteractMsg;
import com.cqupt.model.dto.MsgDTO;
import com.cqupt.model.vo.ImageCheck;
import com.cqupt.model.vo.ResultCodeEnum;
import com.cqupt.model.vo.ScoreVO;
import com.cqupt.service.FileService;
import com.cqupt.service.SparkService;
import com.cqupt.service.SpeakService;
import com.cqupt.utils.*;
import com.iflytek.cloud.speech.*;
import io.github.briqt.spark4j.SparkClient;
import io.github.briqt.spark4j.constant.SparkApiVersion;
import io.github.briqt.spark4j.model.SparkMessage;
import io.github.briqt.spark4j.model.SparkSyncChatResponse;
import io.github.briqt.spark4j.model.request.SparkRequest;
import jakarta.annotation.Resource;
import okhttp3.WebSocket;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Map;

@Service
public class SparkServiceImpl implements SparkService {
    private static String imageUrl = "https://audit.iflyaisol.com//audit/v2/image";
    private static String textUrl = "https://audit.iflyaisol.com//audit/v2/syncText";

    private static final String url = "https://audit.iflyaisol.com/audit/v2/audio";
    private static final String queryUrl = "https://audit.iflyaisol.com/audit/v2/query";
    private static final String APPID = "a830a3cb";
    private static final String APIKey = "6a73cfa8e86bde2f546004fcdff19224";
    private static final String APISecret = "OWQzNmFkN2U3NmFmMjE5NTAyOTU4MTNl";

    private static final String APPID2 = "735d4948";
    private static final String APIKey2 = "cd673d8cdccb0d59b3220a9f2cc12e08";
    private static final String APISecret2 = "MDllZmEzM2Y3ZmVjNmZjYmFhYTIxMmU2";

    //    private static final String lib_ids_1 = "lib_id"; // 根据自己创建获取词库ID  黑名单
    private static final String lib_ids_2 = "xxx"; // 根据自己创建获取词库ID  白名单
    private Object lock = new Object();


    @Resource
    private SparkClient sparkClient;

    @Resource
    private BigModelConfig bigModelConfig;

    @Resource
    private BigModelStreamClient bigModelStreamClient;

    @Resource
    private MemoryUserRecordSpace memoryUserRecordSpace;

    @Resource
    private FileService fileService;

    public String sendMessageToSpark(final String content) {
        List<SparkMessage> messages = new ArrayList<>();
        messages.add(SparkMessage.userContent(content));
        // 构造请求
        SparkRequest sparkRequest = SparkRequest.builder()
                // 消息列表
                .messages(messages)
                // 模型回答的tokens的最大长度,非必传,取值为[1,4096],默认为2048
                .maxTokens(2048)
                // 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高 非必传,取值为[0,1],默认为0.5
                .temperature(0.2)
                // 指定请求版本 这个版本根据自己的 API 版本进行修改
                .apiVersion(SparkApiVersion.V3_5)
                .build();
        // 同步调用
        SparkSyncChatResponse chatResponse = sparkClient.chatSync(sparkRequest);
        String responseContent = chatResponse.getContent();
        return responseContent;
    }

    @Override
    public String chat(int ids, String message) {
        Long id = (long) ids;
        if (StrUtil.isBlank(message)) {

            throw new BusinessException(ResultCodeEnum.DATA_ERROR);
//            return "无效问题，请重新输入";
        }

        // 尝试锁定用户
        if (!memoryUserRecordSpace.tryLock(id)) {
            throw new BusinessException(ResultCodeEnum.DATA_ERROR);
//            return "正在处理上次问题，请稍后再试";
        }

        // 获取连接令牌
        if (!bigModelStreamClient.operateToken(bigModelStreamClient.GET_TOKEN_STATUS)) {
            // 释放锁
            memoryUserRecordSpace.unLock(id);
            throw new BusinessException(ResultCodeEnum.DATA_ERROR);
//            return "当前大模型连接数过多，请稍后再试";
        }

        MsgDTO msgDTO = MsgDTO.createUserMsg(message);
        BigModelWebSocketListener listener = new BigModelWebSocketListener();
        // 组装上下文内容发送
        List<MsgDTO> msgList = memoryUserRecordSpace.getAllInteractMsg(id);

        msgList.add(msgDTO);
        WebSocket webSocket = bigModelStreamClient.sendMsg(UUID.randomUUID().toString().substring(0, 10), msgList, listener);
        if (webSocket == null) {
            // 归还令牌
            bigModelStreamClient.operateToken(bigModelStreamClient.BACK_TOKEN_STATUS);
            // 释放锁
            memoryUserRecordSpace.unLock(id);
            throw new BusinessException(ResultCodeEnum.DATA_ERROR);
//            return "系统内部错误，请联系管理员";
        }
        try {
            int count = 0;
            // 为了避免死循环，设置循环次数来定义超时时长
            int maxCount = bigModelConfig.getMaxResponseTime() * 5;
            while (count <= maxCount) {
                Thread.sleep(200);
                if (listener.isWsCloseFlag()) {
                    break;
                }
                count++;
            }
            if (count > maxCount) {
                throw new BusinessException(ResultCodeEnum.DATA_ERROR);
//                return "大模型响应超时，请联系管理员";
            }
            // 将记录添加到 memoryUserRecordSpace
            String answer = listener.getAnswer().toString();
            memoryUserRecordSpace.storeInteractMsg(id, new InteractMsg(MsgDTO.createUserMsg(message), MsgDTO.createAssistantMsg(answer)));
            return answer;
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            // 关闭连接
            webSocket.close(1000, "");
            // 释放锁
            memoryUserRecordSpace.unLock(id);
            // 归还令牌
            bigModelStreamClient.operateToken(bigModelStreamClient.BACK_TOKEN_STATUS);
        }
    }

    @Override
    public String recognize(MultipartFile file) {
        // 判断文件类型
        byte[] audioData;
        try {
            audioData = AudioUtil.castToPcm(file);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        try {
//            byte[] audioData = file.getBytes();
            String result = recognizeSpeech(audioData);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException();
        }

    }


    @Override
    public byte[] synthesize(String text) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        SpeechUtility.createUtility("appid=" + APPID);

        // 创建合成对象
        SpeechSynthesizer speechSynthesizer = SpeechSynthesizer.createSynthesizer();
        // 设置发音人
        speechSynthesizer.setParameter(SpeechConstant.VOICE_NAME, "aisjiuxu");
        // 设置音频编码
        speechSynthesizer.setParameter(SpeechConstant.AUDIO_FORMAT, "pcm");

        // 启用合成音频流事件
        speechSynthesizer.setParameter(SpeechConstant.TTS_BUFFER_EVENT, "1");

        SynthesizeToUriListener listener = new SynthesizeToUriListener() {
            @Override
            public void onBufferProgress(int progress) {
                // 可以在这里处理合成进度
            }

            @Override
            public void onSynthesizeCompleted(String uri, SpeechError error) {
                if (error == null) {
                    try (InputStream inputStream = new FileInputStream("./test.pcm")) {
                        if (inputStream != null) {
                            byte[] buffer = new byte[1024];
                            int len;
                            while ((len = inputStream.read(buffer)) != -1) {
                                outputStream.write(buffer, 0, len);
                            }
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        throw new RuntimeException(e);
                    }
                } else {
                    error.printStackTrace();
                }
                synchronized (this) {
                    this.notify();
                }
            }

            @Override
            public void onEvent(int eventType, int arg1, int arg2, int arg3, Object obj1, Object obj2) {
                // 合成事件回调
            }
        };

        // 开始合成
        speechSynthesizer.synthesizeToUri(text, "./test.pcm", listener);

        // 等待合成完成
        synchronized (listener) {
            try {
                listener.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        return outputStream.toByteArray();
    }

    @Override
    public String checkImageCompliance(MultipartFile file) {
        try {
            byte[] bytes = ImageCheckUtil.imageToByteArray(file);
            String json = "{\n" +
                    "  \"content\": \"" + Base64.getEncoder().encodeToString(bytes) + "\"\n" +
                    "}";

            // 2、获取鉴权
            Map<String, String> urlParams = ImageCheckUtil.getAuth(APPID2, APIKey2, APISecret2);

            ///3、发起请求
            return ImageCheckUtil.doPostJson(imageUrl, urlParams, json);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String checkText(String text) {
        String libId = TextCheckUtil.getLibId();
        System.out.println(libId);
        /**
         * 业务参数
         * --- 如果需要使用黑白名单资源，放开lib_ids与categories参数
         * */
        String json = "{\n" +
                "  \"is_match_all\": 1,\n" +
                "  \"content\": \"" + text + "\",\n" + // 放开lib_ids与categories参数，注意在content后面加逗号使之成为合法json
                "  \"lib_ids\": [\n" +
                "    \"" + libId + "\"\n" + // 放开下面的话记得加,
//                "    \"" + libId + "\"\n" +
                "  ],\n" +
                "  \"categories\": [\n" +
                "    \"pornDetection\",\n" +
                "    \"violentTerrorism\",\n" +
                "    \"political\",\n" +
                "    \"lowQualityIrrigation\",\n" +
                "    \"contraband\",\n" +
                "    \"advertisement\",\n" +
                "    \"uncivilizedLanguage\"\n" +
                "  ]\n" +
                "}";
        // 获取鉴权
        Map<String, String> urlParams = null;
        try {
            urlParams = TextCheckUtil.getAuth(APPID2, APIKey2, APISecret2);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        // 发起请求
        String returnResult = TextCheckUtil.doPostJson(textUrl, urlParams, json);
        return returnResult;
    }

    @Override
    public ScoreVO evaluate(MultipartFile file, String text) {
        try (InputStream inputStream = file.getInputStream()) {
            IseEvaluation evaluator = new IseEvaluation(text, inputStream);
            String result = evaluator.evaluate();
            System.out.println(result);
            return XmlParserUtil.getResult3(result);

        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    @Override
    public byte[] tts(String text) {
        try {
            byte[] res = TtsUtil.tts(text);
//            System.out.println(res);
            return res;
        } catch (Exception e) {

            e.printStackTrace();
            throw new RuntimeException(e);
        }

    }

    @Override
    public String translate(String text) {

        try {
            String res = MachineTranslationUtil.translate(text);
            return res;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    @Override
    public String audioCheck(MultipartFile file) {
        String url = fileService.upload(file);
        // 1、业务参数，注意notify_url替换自己的回调地址
        System.out.println(url.substring(url.length() - 36));
        String json = "{\n" +
                "  \"audio_list\": [\n" +
                "    {\n" +
                "      \"audio_type\": \"" + "mp3" + "\",\n" +
                "      \"file_url\": \"" + url + "\",\n" +
                "      \"name\": \"" + "xxx" + url.substring(url.length() - 36) + "\"\n" +
                "    }\n" +
                "  ],\n" +
                "  \"notify_url\": \"http://localhost:6868/api/spark/video_callback\"\n" +
                "}";
        // 2、获取鉴权
        Map<String, String> urlParams = null;
        try {
            urlParams = AudioCheckUtil.getAuth(APPID2, APIKey2, APISecret2);
            ///3、发起请求
            String returnResult = AudioCheckUtil.doPostJson(this.url, urlParams, json);
            System.out.println("请到回调地址或存储库查看结果，调用信息如下：\n" + returnResult);
            String request_id = (String) JSONObject.parseObject(JSONObject.parseObject(returnResult).get("data").toString()).get("request_id");
            //4、拿到request_id后主动查询合规结果
            while (true) {
                String queryJson = "{\n" +
                        "  \"request_id\": \"" + request_id + "\"\n" +
                        "}";
                urlParams = AudioCheckUtil.getAuth(APPID2, APIKey2, APISecret2);
                returnResult = AudioCheckUtil.doPostJson(queryUrl, urlParams, queryJson);
                System.out.println(returnResult);
                Integer finishIndex = (Integer) JSONObject.parseObject(JSONObject.parseObject(returnResult).get("data").toString()).get("audit_status");
                if (finishIndex == 0) {
                    System.out.println("音频合规待审核...");
                }
                if (finishIndex == 1) {
                    System.out.println("音频合规审核中...");
                }
                if (finishIndex == 2) {
                    System.out.println("音频合规审核完成：");
                    System.out.println(returnResult);
                    break;
                }
                if (finishIndex == 4) {
                    System.out.println("音频合规审核异常：");
                    System.out.println(returnResult);
                }
                Thread.sleep(3000);

            }
            return returnResult;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }


    private String recognizeSpeech(byte[] audioData) throws Exception {
        // 设置科大讯飞的AppID
        SpeechUtility.createUtility("appid=a830a3cb");
        SpeechRecognizer recognizer = SpeechRecognizer.createRecognizer();
        recognizer.setParameter(SpeechConstant.AUDIO_SOURCE, "-1");
        recognizer.setParameter(SpeechConstant.RESULT_TYPE, "plain");
        recognizer.setParameter(SpeechConstant.LANGUAGE, "en_us"); // 识别英语
        recognizer.setParameter(SpeechConstant.VAD_BOS, "5000");//前端点超时，
        recognizer.setParameter(SpeechConstant.VAD_EOS, "10000");


        StringBuilder result = new StringBuilder();

        RecognizerListener listener = new RecognizerListener() {

            @Override
            public void onVolumeChanged(int i) {
            }

            @Override
            public void onBeginOfSpeech() {
            }

            @Override
            public void onEndOfSpeech() {
            }

            @Override
            public void onResult(RecognizerResult recognizerResult, boolean isLast) {
                result.append(recognizerResult.getResultString());
                if (isLast) {
                    synchronized (lock) {
                        lock.notify();
                    }
                }
            }

            @Override
            public void onError(SpeechError error) {
                System.out.println(System.getProperty("user.dir"));
                System.out.println(error.getErrorCode());
                synchronized (lock) {
                    lock.notify();
                }
            }

            @Override
            public void onEvent(int eventType, int arg1, int arg2, String msg) {
            }
        };

        recognizer.startListening(listener);

        recognizer.writeAudio(audioData, 0, audioData.length);
        recognizer.stopListening();

        synchronized (lock) {
            lock.wait();
        }

        return result.toString();
    }


}
